home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_gnats.idb / usr / freeware / lib / gnats / contrib / prmon.z / prmon
Encoding:
Text File  |  1999-04-16  |  12.0 KB  |  501 lines

  1. #!/usr/unsupported/bin/bash
  2.  
  3. function usage ()
  4. {
  5.     if [ $# -gt 0 ]; then
  6.        echo -e "${progname}: $*\n" 1>&2
  7.     fi
  8.  
  9.     shcat 1>&2 <<EOF
  10. Usage: ${progname} {-D} {-e maintainer} {-h} {-t time} {-v} {-x d1} {-x d2}
  11.     {-x ...} [dir1] {dir2} {...}
  12.     {--debug} {--errors-to=maintainer} {--help} {--time=time} {--verbose}
  13.     {--except d1} {--except d2} {--except ...} [dir1] {dir2} {...}
  14.  
  15.    Options in braces are optional.  Those in brackets are required. 
  16.  
  17.    Time specifications must have units after the value, e.g. "d" for days,
  18.    "m" for minutes.  Thus, a valid time string might be "1w2d12h" which
  19.    means look for files older than 1 week, 2 days, and 12 hours (9.5 days).
  20.  
  21.    Default time is 1 day.  Default maintainer is ${default_maintainer}.
  22.  
  23. -D, --debug                  Turn on shell debugging ("set -x").
  24. -e, --errors-to MAINTAINER   Stderr is reported to maintainer via email.
  25.                              If MAINTAINER is set to "maintainer", the
  26.                              default maintainer is assumed (see above). 
  27. -h, --help                   You're looking at it.
  28. -t, --time TIME              Only PRs older than TIME will be checked
  29.                  for locks.  See above for info on time format. 
  30. -v, --verbose                Chatter away while working.
  31. -x, --except                 subdirectories not to search.  
  32.  
  33. EOF
  34.  
  35.     exit 1
  36. }
  37.  
  38. function main ()
  39. {
  40.  local find_args="-mtime"
  41.  local seconds
  42.  local age_unit="day"
  43.  local age
  44.  local except_list 
  45.  
  46.     initialize_variables "$@"
  47.     parse_command_args "$@"
  48.     shift $?
  49.  
  50.     test -n "${debug+set}" && set -x
  51.  
  52.     if [ $# -eq 0 ]; then
  53.        usage "Specify at least one pathname"
  54.     fi
  55.  
  56.     if ! seconds=$(timefmt_to_seconds "${time}") ; then
  57.        exit 1
  58.     fi
  59.  
  60.     age=$(seconds_to_days ${seconds})
  61.     if [ -z "${age}" -o "${age}" -lt 1 ]; then
  62.        find_args="-mmin"
  63.        age_unit="minute"
  64.        age=$(seconds_to_minutes ${seconds})
  65.     fi
  66.  
  67.     if [ -n "${exceptions}" ]; then
  68.        # Construct exception list
  69.        for dir in ${exceptions} ; do
  70.           except_list="${except_list} ! -path ${dir}/*"
  71.        done
  72.     fi
  73.  
  74.     ( 
  75.       set -o noglob; 
  76.       exec find "$@" ${except_list} -type f -name "*.lock" ${find_args} "+${age}" -print
  77.     ) | find_locks
  78.  
  79.     send_reports
  80.     mail_stderr_to_maintainer
  81. }
  82.  
  83. function initialize_variables ()
  84. {
  85.     # Bash magic.  If this variable is bound, then globbing patterns which
  86.     # don't actually match anything will result in nothing at all, rather
  87.     # than the pattern themselves. 
  88.     allow_null_glob_expansion=
  89.  
  90.     progname="${0##*/}"
  91.     progname_arguments="$*"  # Save them for stderr report
  92.  
  93.     bq="\`"  # To prevent hairy quoting and escaping later.
  94.     eq="'"
  95.  
  96.     # Path should be able to catch sendmail, which is usually in /usr/lib
  97.     export PATH="/usr/latest/bin:${PATH}:/usr/lib"
  98.  
  99.     default_maintainer="brendan"
  100.     maintainer="${default_maintainer}"
  101.  
  102.     time=1d       # Complain about locks older than 1 day, by default. 
  103.  
  104.     hostname=$(hostname)
  105.  
  106.     tmpdir="/tmp/${progname}$$"
  107.     while [ -e "${tmpdir}" ]; do
  108.        tmpdir="/tmp${progname}${RANDOM}"
  109.     done
  110.  
  111.     TRAP_SIGNALS="EXIT SIGHUP SIGINT SIGQUIT SIGTERM"
  112.     trap 'cleanup_and_exit' ${TRAP_SIGNALS}
  113.  
  114.     mkdir ${tmpdir} || exit 1
  115. }
  116.  
  117. function parse_command_args ()
  118. {
  119.  local orig_number_options=$#
  120.  local except
  121.  
  122.     # unset option variables to make sure they weren't accidentally
  123.     # exported 
  124.     unset debug stderr_file verbose exceptions
  125.  
  126.     # If you add new options be sure to change the wildcards below to make
  127.     # sure they are unambiguous (i.e. only match one possible long option)
  128.     # Be sure to show at least one instance of the full long option name to
  129.     # document what the long option is canonically called. 
  130.     # Long options which take arguments will need a `*' appended to the
  131.     # canonical name to match the value appended after the `=' character. 
  132.     while [ $# -gt 0 ]; do
  133.        case z$1 in
  134.           z-D | z--debug | z--d* )
  135.              debug=t
  136.              shift
  137.             ;;
  138.           z-e | z--errors-to* | z--er* )
  139.              get_option_argument maintainer "$1" "$2"
  140.              shift $?
  141.  
  142.              if [ "${maintainer}" = "maintainer" ]; then
  143.                 maintainer="${default_maintainer}"
  144.              fi
  145.  
  146.              # Redirect all of stderr to a tmp file which we can mail
  147.              # later. 
  148.              stderr_file="/tmp/${progname}.stderr$$"
  149.              exec 2> "${stderr_file}"
  150.             ;;
  151.           z-h* | z--help | z--h* )
  152.              usage
  153.             ;;
  154.           z-t | z--time | z--t* )
  155.              get_option_argument time "$1" "$2"
  156.              shift $?
  157.             ;;
  158.           z-v | z--verbose | z--v* )
  159.              verbose=t
  160.              shift
  161.             ;;
  162.           z-x | z--except | z--ex* )
  163.              unset except
  164.              get_option_argument except "$1" "$2"
  165.              shift $?
  166.  
  167.              exceptions="${exceptions} ${except}"
  168.             ;;
  169.           z-- )
  170.              shift
  171.              break
  172.             ;;
  173.           z-* )
  174.              usage "${bq}${1}${eq} is not a valid option."
  175.             ;;
  176.           * )
  177.              break
  178.             ;;
  179.        esac
  180.     done
  181.  
  182.     # Return number of shifted arguments so calling function can shift
  183.     # appropriate amount.
  184.     return $[ orig_number_options - $# ]
  185. }
  186.  
  187. # Usage: get_option_argument VARIABLE OPTION ARG {OPTIONAL}
  188. #    where VARIABLE is shell variable that will be set to the value ARG.
  189. #    Long option syntax is `--foo=bar' or `--foo bar'.  3rd argument ARG
  190. #    won't get used if first long option syntax was used.  If 4 arg
  191. #    OPTIONAL is non-empty, option isn't required to have an argument; if
  192. #    the argument is missing, VARIABLE is set to the empty value. 
  193. # Returns number of positions caller should shift
  194. function get_option_argument ()
  195. {
  196.  local variable="$1"
  197.  local option="$2"
  198.  local arg="$3"
  199.  local arg_optional="$4"
  200.  
  201.     # All long options must be at least 3 characters long (--o*), whereas
  202.     # short options are only two chars (-o) and arguments are always
  203.     # separate.
  204.     if [ ${#option} -ge 3 -a "z${option#*=}" != "z${option}" ]; then
  205.        arg="${option#*=}"  # Strip off anything before and including `=' char
  206.        eval ${variable}=\'"${arg}"\'
  207.        return 1
  208.     else
  209.        if [ -z "${arg}" -a -z "${arg_optional}" ]; then
  210.           usage "option ${bq}${option}${eq} requires argument."
  211.        fi
  212.        eval ${variable}=\'"${arg}"\'
  213.        return 2
  214.     fi
  215. }
  216.  
  217. cleanup_and_exit ()
  218. {
  219.  local exitstat="$?"
  220.  
  221.    # Reset traps to avoid double execution of this function when a signal
  222.    # is caught (as opposed to normal exit).
  223.    trap '' ${TRAP_SIGNALS}
  224.  
  225.    rm -rf "${tmpdir}" ${stderr_file} 2> /dev/null
  226.  
  227.    builtin exit ${exitstat}
  228. }
  229.  
  230. #
  231. # Time-related functions
  232. #
  233.  
  234. # Compute the number of seconds specified by a string of the form
  235. #    ...y...M...w...d...h...m...s
  236. # For years, months, weeks, days, hours, minutes, and seconds.  Any given
  237. # unit is optional. 
  238. function timefmt_to_seconds ()
  239. {
  240.  local arg="$*"
  241.  local val
  242.  local unit
  243.  local total
  244.  
  245.     if [ $# -eq 0 ]; then
  246.        read arg
  247.     fi
  248.  
  249.     shift $#
  250.     set -- $(echo ${arg} | sed 's/\([0-9]*\)\([yMwdhms]\)/\1 \2 /g')
  251.  
  252.     while [ $# -gt 0 ]; do
  253.        val="${1}"
  254.        unit="${2}"
  255.        shift 2
  256.  
  257.        # `multiple' is number of seconds per unit (year, month, etc.)
  258.        case "${unit}" in
  259.           y )      multiple=31536000 ;;
  260.           M )      multiple=2592000  ;;
  261.           w )      multiple=604800   ;;
  262.           "" | d ) multiple=86400    ;;
  263.           h )      multiple=3600     ;;
  264.           m )      multiple=60       ;;
  265.           s )      multiple=1        ;;
  266.           * )
  267.              usage "Invalid time+unit argument: ${val}${unit}"
  268.              return 1
  269.             ;;
  270.        esac
  271.  
  272.        total=$[ total + (val * multiple) ];
  273.     done
  274.  
  275.     echo "${total}"
  276. }
  277.  
  278. function seconds_to_days ()
  279. {
  280.     echo $[ ${1:-0} / 86400 ];
  281. }
  282.  
  283. function seconds_to_minutes ()
  284. {
  285.    echo $[ ${1:-0} / 60];
  286. }
  287.  
  288. # Routines which search for locks
  289. #
  290.  
  291. function find_locks ()
  292. {
  293.     verbose_echo "Searching for locks..."
  294.  
  295.     while read file; do
  296.        verbose_echo "Looking for locks in ${file}"
  297.  
  298.        lock_info=$(gnatslocks "${file}")
  299.  
  300.        if [ -n "${lock_info}" ]; then
  301.       file="`echo ${lock_info%%:*} | sed -e s,/gnats/GNATS/,,g`"
  302.           user="${lock_info#*:\ }"
  303.       user="${user%%@*}"
  304.  
  305.           verbose_echo "*** PR ${file} is locked by ${bq}${user}${eq}"
  306.  
  307.           if user_known_p "${user}" ; then
  308.              echo "    ${file}" >> "${tmpdir}/${user}.K"
  309.           else
  310.              owner=$(ls -l "${file}" | awk '{print $3}')
  311.              echo "${file} by ${user}" >> "${tmpdir}/${owner}.U"
  312.           fi
  313.        fi
  314.  
  315.     done
  316.  
  317.     verbose_echo "Done searching for locks."
  318. }
  319.  
  320. # Give some info about each lock.
  321. function gnatslocks ()
  322. {
  323.  local file
  324.  
  325.     for file in "$@" ; do
  326.        filename=`echo ${file} | sed -e s/\.lock//g`
  327.        echo "${filename}: `cat ${file}`"
  328.     done
  329. }
  330.  
  331. function user_known_p ()
  332. {
  333.  local file
  334.  
  335.     for file in /etc/passwd ; do
  336.        if grep -s "^${1}:" "${file}" > /dev/null 2>&1 ; then
  337.           return 0
  338.        fi
  339.     done
  340.  
  341.     return 1
  342. }
  343.  
  344. #
  345. # Reporting generating routines
  346. #
  347.  
  348. function send_reports ()
  349. {
  350.  local file
  351.  
  352.     pushd "${tmpdir}" > /dev/null
  353.  
  354.     for file in *.[KU] ; do
  355.        verbose_echo "*** Mailing report to ${file%.[KU]}"
  356.        generate_mail_message "${file}" | sendmail -oi -t
  357.     done
  358.  
  359.     popd > /dev/null
  360. }
  361.  
  362. function generate_mail_message ()
  363. {
  364.  local file="$1"
  365.  local recipient="${file%.[KU]}"
  366.  local line
  367.    
  368.     shcat <<- __EOF__
  369.     From: ${progname} (GNATS lock monitor daemon)
  370.     To: ${recipient}
  371.     Bcc: ${maintainer}
  372.     Reply-To: ${maintainer}
  373.     Subject: old GNATS locks
  374.     Precedence: bulk
  375.     
  376.     This is an automated report generated on ${hostname}. 
  377.     
  378.     __EOF__
  379.  
  380.     case "${file}" in
  381.       "*.K" )
  382.          shcat <<- __EOF__
  383.     You have had some PRs locked for over $(age_echo ${age} ${age_unit}).
  384.     If you no longer need the PR locked, just do \'C-x k\' in the buffer
  385.     to relinquish the lock.  However, if the original emacs session you
  386.     had no longer exists (e.g, emacs crashed, or you did a kill-buffer
  387.     by hand, you have a couple of options.  You can either type:
  388.  
  389.         /usr/unsupported/lib/gnats/pr-edit --unlock foo/1234
  390.  
  391.     for each one, or, in emacs, you can do
  392.  
  393.         M-x unlock-pr RET foo/1234 RET
  394.  
  395.     to unlock them.  The PRs in question are:
  396.     __EOF__
  397.        ;;
  398.       "*.U" )
  399.          shcat <<- __EOF__
  400.     The following files are presently locked by unknown users, but 
  401.     you (${recipient}) are the present owner of the PR.  These files 
  402.     have been locked over $(age_echo ${age} ${age_unit}).  Please look into this.
  403.     __EOF__
  404.         ;;
  405.     esac
  406.  
  407.     echo ""
  408.     shcat "${file}"
  409.     echo ""
  410.     shcat <<- __EOF__
  411.     Thanks,
  412.     Your friendly neighborhood GNATS admin.
  413.     __EOF__
  414.  
  415. }
  416.  
  417. function age_echo ()
  418. {
  419.  local age="$1"
  420.  local unit="$2"
  421.  
  422.     echo -n "${age} "
  423.     if [ "${age}" = "1" ]; then
  424.        echo "${unit}"
  425.     else
  426.        echo "${unit}s"
  427.     fi
  428. }
  429.  
  430. function mail_stderr_to_maintainer ()
  431. {
  432.     if [ -z "${stderr_file+set}" ]; then return 0; fi
  433.  
  434.     if [ -s "${stderr_file}" ]; then 
  435.        sendmail -oi -t <<- __EOF__
  436.     From: ${progname} (GNATS lock monitor daemon)
  437.     To: ${maintainer}
  438.     Subject: ${progname} stderr output
  439.     Precedence: bulk
  440.     
  441.     This is an automated report from host ${hostname}.
  442.     With euid ${EUID} (ruid ${UID}), program "${progname}" ran with 
  443.     the following arguments:
  444.         
  445.        ${progname_arguments}
  446.         
  447.     and generated the following output on stderr:
  448.         
  449.     $(cat "${stderr_file}")
  450.     __EOF__
  451.     fi
  452. }
  453.  
  454. function shcat ()
  455. {
  456.  local IFS=""
  457.  local line
  458.  local file
  459.  local exitstat=0
  460.  
  461.     if [ $# -eq 0 ]; then
  462.        while read line; do
  463.           echo "${line}"
  464.        done
  465.        return 0
  466.     else
  467.        for file in "$@" ; do
  468.           if [ -r "${file}" ]; then
  469.              { 
  470.                while read line; do
  471.                   echo "${line}"
  472.                done
  473.              } < "${file}"
  474.           else
  475.              # This will cause the error to be printed on stderr
  476.              < "${file}"
  477.              exitstat=1
  478.           fi
  479.        done 
  480.        return ${exitstat}
  481.     fi
  482. }
  483.  
  484. function verbose_echo ()
  485. {
  486.     test -n "${verbose+set}" && echo "$@" 1>&2
  487. }
  488.  
  489. function exit ()
  490. {
  491.     exitstat="$1"
  492.     builtin exit ${exitstat}
  493. }
  494.  
  495. main "$@"
  496.  
  497. # eof
  498. #
  499.